home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1997 August / Walnut Creek CDROM.7z / VOL_400 / 466_01 / SRC / EXTRACT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-20  |  39.3 KB  |  1,503 lines

  1. /*
  2.  * extract.cpp
  3.  */
  4.  
  5. #include <afx.h>
  6. #include <afxtempl.h>
  7. #include <io.h>
  8. #include "parse.h"
  9. #include "topiclog.h"
  10. #include "fmtspec.h"
  11. #include "docexpr.h"
  12. #include "cmdargs.h"
  13. #include "extract.h"
  14. #include "errmsg.h"
  15. #include "parsetag.h"
  16.  
  17. //char szEmptyField[] = "Empty Field";
  18.  
  19. void ExtractFile(CFile &out, const char *szFilename, CTopicLog  &log, RUNOPTIONS &run, FormatInfo &fmt);
  20. BOOL ExtractTopic(CSrcInput &in, CTopicLog &log, RUNOPTIONS &run, FormatInfo   &fmt, CFile &out);        
  21.  
  22. const char *EatNoiseChars(const char * szLine);
  23. int IsBeginTopic(const char *szTag, FormatInfo &fmt);
  24. int IsEndComment(const char *sz, int nCommentType);
  25. void CheckCurTag(CSrcInput &in, CTopicLog &log, CFile &out, FormatInfo &fmt, RUNOPTIONS &run, int fAllowPending);
  26. int AddLineToBuffer(char *szLine, CSrcInput &in);
  27. const char *GetTag(CString &sTagDest, const char *szSrc);
  28. void AddTopic(CTopicLog &log, CTagList &listTags, CSrcInput &in, CFile &out, FormatInfo &fmt, RUNOPTIONS &run);
  29.  
  30.  
  31. /**********************************************************************/
  32.  
  33. CSrcInput::CSrcInput()
  34. {
  35.     ZeroMem(m_achCommentStart, MAXLINEBUFSIZE+1);
  36.     ZeroMem(m_achTagBuf, TAGBUFSIZE+1);
  37.     m_szTagCur = m_achTagBuf;
  38.     m_szParseCur = m_szLineBuf;
  39.     m_nSourceType = -1;
  40.     m_nCommentType = -1;
  41.     m_chCommentDelim = 0;
  42.     ResetState();
  43.     m_nFile = 0;
  44. };
  45.  
  46.  
  47. CSrcInput::~CSrcInput(void)
  48. {
  49.     ASSERT(m_pendMain.IsEmpty());
  50.     ASSERT(m_pendAux.IsEmpty());
  51.     ASSERT(m_listAuxTags.IsEmpty());
  52. }
  53.  
  54. void CSrcInput::CueParse(CPendingList &pend, CTag *ptag, int nTargetFields, int nParseType, BOOL bTopic)
  55. {
  56.     CPendingTag *pnew = new CPendingTag;
  57.     pnew->nParseType = nParseType;
  58.     pnew->nPendingFields = nTargetFields;
  59.     pnew->bTopic = bTopic;
  60.     pnew->ptag = ptag;
  61.  
  62.     pend.AddTail(pnew);    
  63. };
  64.  
  65. void CSrcInput::CueParse(CPendingList &pend, CTag *ptag, CTag *ptagParent, FormatInfo &fmt)
  66. {
  67.     CPendingTag *pnew = new CPendingTag;
  68.     pnew->ptag = ptag;
  69.     pnew->ptagParent = ptagParent;
  70.     pnew->bTopic = TRUE;
  71.  
  72.     CFmtSrchTopic srch;
  73.     srch.m_sName = ptag->sTag;
  74.     CFmtTopic *pfmtTopic = (CFmtTopic *)fmt.topic.Get(&srch);
  75.     if(pfmtTopic)
  76.     {
  77.         pnew->nParseType = pfmtTopic->GetParseType();
  78.         pnew->nPendingFields = pfmtTopic->GetNumFields();
  79.     }
  80.  
  81.     pend.AddTail(pnew);    
  82.  
  83. }
  84.  
  85. void CSrcInput::EmptyParse(void)
  86. {
  87.     POSITION pos;
  88.     CPendingTag *p;
  89.  
  90.     pos = m_pendMain.GetHeadPosition();
  91.     while(pos)
  92.     {
  93.         p = m_pendMain.GetNext(pos);
  94.         delete p;
  95.     }
  96.     pos = m_pendAux.GetHeadPosition();
  97.     while(pos)
  98.     {
  99.         p = m_pendAux.GetNext(pos);
  100.         delete p;
  101.     }
  102.     m_pendMain.RemoveAll();
  103.     m_pendAux.RemoveAll();
  104. }
  105.  
  106.  
  107. /*****************************************************************************/
  108.  
  109. //@func Determines whether the specificed line has only a template
  110. // definition and nothing else. A partial template definition applies.
  111. //
  112. //@rdesc Returns 1 if the line contains only a full or partial template
  113. // definition; 0 if no template definition or if there is other 
  114. // stuff on the line.
  115.  
  116. int IsTemplateLine(const char *szLine)
  117. {
  118.     szLine = EatWhite(szLine);
  119.  
  120.     if(strncmp(szLine, "template", 8))
  121.         return 0;
  122.  
  123.     szLine = EatWhite(szLine+8);
  124.     if(*szLine != chOpenAngle)
  125.         return 1;
  126.  
  127.     szLine = MatchParen(szLine, chCloseAngle);
  128.     if(*szLine != chCloseAngle)
  129.         return 1;
  130.  
  131.     szLine = EatWhite(++szLine);
  132.  
  133.     if(isidchar(*szLine))
  134.         return 0;
  135.  
  136.     return 1;
  137. }
  138.  
  139.  
  140.  
  141. int TransferAux(
  142.     CPendingTag *ppend,
  143.     CTag *ptagTopic,
  144.     FormatInfo  &fmt)
  145. {
  146.     CFmtSrchTag srch;
  147.     CFmtPara *pfmt;
  148.     NAMETOKEN *pname = NULL;
  149.     char *szField;
  150.     int nLen;
  151.  
  152.     srch.m_sName = ppend->ptagParent->sTag;
  153.  
  154.     pfmt = (CFmtPara *)fmt.paragraph.Get(&srch);
  155.     if(pfmt == NULL)
  156.         return 1;
  157.  
  158.     pname = pfmt->GetMap(ppend->ptag->sTag);
  159.     if(NULL == pname)
  160.         return 1;
  161.  
  162.     for(int i = 0; i < MAXNUMFIELDS && pname[i].nType != nameNone; i++)
  163.     {
  164.         switch(pname[i].nType)
  165.         {
  166.         case nameLocal:
  167.             szField = ppend->ptagParent->aszFieldText[pname[i].nField];
  168.             nLen = ppend->ptagParent->anFieldLen[pname[i].nField];
  169.             break;
  170.  
  171.         case nameTopic:
  172.             szField = ptagTopic->aszFieldText[pname[i].nField];
  173.             nLen = ptagTopic->anFieldLen[pname[i].nField];
  174.             break;
  175.  
  176.         default:
  177.             return 1;
  178.         }
  179.  
  180.         if(nLen)
  181.         {
  182.             ppend->ptag->aszFieldText[i] = new char[nLen+1];
  183.             strcpy(ppend->ptag->aszFieldText[i], szField);
  184.             ppend->ptag->anFieldLen[i] = nLen;
  185.         }
  186.     }
  187.  
  188.     ppend->ptag->nFields = i;
  189.  
  190.     return 0;
  191. }
  192.  
  193. /*
  194. @func Parses source text for any tags requiring field information
  195. from source statements.
  196.  
  197. @parm Input state - including parsing wannabe lists, which are emptied
  198. if parsing is successful.
  199.  
  200. @parm Topic log
  201.  
  202. @parm Format file
  203.  
  204. */
  205.  
  206. void CheckCurParse(
  207.    CSrcInput &in,
  208.    CTopicLog &log,
  209.    FormatInfo &fmt)
  210. {
  211.     int nRet;
  212.     POSITION pos, posCur;
  213.     CPendingTag *ppend;
  214.     CParseText txt(in.m_szLineBuf, in.m_szParseCur);
  215.     
  216.     // If this line just has "template<...>" on it, then skip it.
  217.     // @todo Parse it!
  218.  
  219.     if(IsTemplateLine(in.m_szLineBuf))
  220.         return;
  221.  
  222.     // Decue main topic parse wannabes
  223.  
  224.     pos = in.m_pendMain.GetHeadPosition();
  225.     while( pos != NULL )
  226.     {
  227.         posCur = pos;
  228.  
  229.         ppend = in.m_pendMain.GetNext( pos );
  230.  
  231.         nRet = ParseSrc(ppend->ptag, txt,
  232.                 in.m_lCurLine, ppend->nParseType, TRUE);
  233.  
  234.         if(warnSrcFieldsMultiline == nRet)
  235.         {
  236.             in.SetAppend();
  237.  
  238.             return;
  239.         }
  240.         else if(nRet)
  241.         {
  242.             PrintError(log.GetSrcFn(), in.m_lCurLine, nRet);
  243.         }
  244.         else
  245.         {
  246.             in.m_szParseCur = in.m_szLineBuf+(txt.m_szCur-txt.m_szBase);
  247.         }
  248.  
  249.         delete ppend;
  250.         
  251.         in.m_pendMain.RemoveAt(posCur);
  252.     }
  253.  
  254.     // Decue aux topic parse wannabes
  255.  
  256.     pos = in.m_pendAux.GetHeadPosition();
  257.     while( pos != NULL )
  258.     {
  259.         posCur = pos;
  260.  
  261.         ppend = in.m_pendAux.GetNext( pos );
  262.  
  263.         if(ppend->bTopic)
  264.         {
  265.             if(TransferAux(ppend, in.m_listTags.GetHead(), fmt))
  266.                 PrintError(log.GetSrcFn(), ppend->ptag->lSrcLineNum, warnCantTransferAux);
  267.  
  268.             if(CFmtTopic::parseMfunc == ppend->nParseType)
  269.                 txt.Set(ppend->ptagParent->aszFieldText[2]);
  270.         }
  271.         else
  272.         {
  273.             nRet = ParseSrc(ppend->ptag, txt,
  274.                 in.m_lCurLine, ppend->nParseType, TRUE);
  275.             if(nRet)
  276.                 PrintError(log.GetSrcFn(), in.m_lCurLine, nRet);
  277.         }
  278.  
  279.         delete ppend;
  280.         
  281.         in.m_pendAux.RemoveAt(posCur);
  282.     }
  283.     in.m_szParseCur = in.m_szLineBuf;
  284. }
  285.  
  286.  
  287.  
  288. /*
  289. @func This function checks whether there's a tag waiting
  290. to be parsed, and parses the tag if one exists. 
  291.  
  292. @rdesc Zero if successful, otherwise an error code.
  293. */
  294.  
  295. void CheckCurTag(
  296.     CSrcInput &in,          //@parm Input file
  297.     CTopicLog &log,            //@parm Topic log - may need to add aux topics
  298.     CFile &out,                //@parm Output temp file - may need to add aux topics
  299.     FormatInfo &fmt,        //@parm Formatting info
  300.     RUNOPTIONS &run,        //@parm Runtime options structure - for verbosity in adding topics
  301.     int fAllowPending)      //@parm TRUE if source info available for parsing
  302. {
  303.     int nRet = 0;
  304.     BOOL bTopic = FALSE;
  305.     CTagList &listTags = in.m_listTags;
  306.  
  307.     // If we have an active tag, parse it.
  308.  
  309.     if(in.m_nState.Tag)
  310.     {
  311.         // If first tag in the topic, it's a topic tag
  312.         if(!in.m_nState.HaveTopicTag)
  313.         {
  314.             in.m_nState.HaveTopicTag = TRUE;
  315.             bTopic = TRUE;
  316.         }
  317.  
  318.         in.m_nState.Tag = FALSE;
  319.  
  320.         // An auxiliary paragraph (@@pgraph) gets parsed and added to the
  321.         // auxiliary tag list. Otherwise, parse the tag and add to the
  322.         // main tag list.
  323.  
  324.         if(in.m_nState.AuxPara)
  325.         {
  326.             nRet = ParseTag(in, fmt, TRUE, NULL, FALSE);
  327.             if(nRet)
  328.             {
  329.                 PrintError(log.GetSrcFn(), in.m_nLineTag, nRet);
  330.                 return;
  331.             }
  332.             
  333.             in.m_nState.AuxPara = FALSE;
  334.         }
  335.         else
  336.         {
  337.             nRet = ParseTag(in, fmt, FALSE, NULL, bTopic);
  338.             if(nRet)
  339.             {
  340.                 PrintError(log.GetSrcFn(), in.m_nLineTag, nRet);
  341.                 return;
  342.             }
  343.  
  344.             // See if an aux topic is available, add it to the log if so.
  345.  
  346.             if(!in.m_listAuxTags.IsEmpty())
  347.             {
  348.                 // Might be an aux topic tag left on the pending cue - this
  349.                 // only occurs when aux topic tag fields were complete and
  350.                 // did not get filled out from source text later in this function.
  351.  
  352.                 if(!in.m_pendAux.IsEmpty())
  353.                 {
  354.                     CPendingTag *ppend = in.m_pendAux.GetHead();
  355.                     if(TransferAux(ppend, in.m_listTags.GetHead(), fmt))
  356.                         PrintError(log.GetSrcFn(), ppend->ptag->lSrcLineNum, warnCantTransferAux);
  357.                     in.m_pendAux.RemoveAll();
  358.                 }
  359.  
  360.                 AddTopic(log, in.m_listAuxTags, in, out, fmt, run);
  361.             }
  362.  
  363.             // AuxTopicOpen - last tag extracted (the one we are dealing with now)
  364.             //                    has an aux topic attached.
  365.             // AuxTopicTag - this line has an aux topic attached.
  366.  
  367.             // If there is an auxiliary topic tag attached to the paragraph,
  368.             // allocate a new tag for it and add it to the parsing cue.
  369.  
  370.             if(in.m_nState.AuxTopicOpen)
  371.             {
  372.                 CTag *ptagAux = new CTag;
  373.                 ptagAux->sTag = in.m_sTagAux;
  374.                 ptagAux->lSrcLineNum = in.m_lCurLine;
  375.  
  376.                 in.m_listAuxTags.AddTail(ptagAux);
  377.  
  378.                 in.CueParse(in.m_pendAux, ptagAux, in.m_listTags.GetTail(), fmt);
  379.  
  380.                 in.m_nState.AuxTopicOpen = FALSE;
  381.             }
  382.         }
  383.     }
  384.  
  385.     if(in.m_nState.AuxParaTag)
  386.     {
  387.         in.m_nState.AuxParaTag = FALSE;
  388.         in.m_nState.AuxPara = TRUE;
  389.     }
  390.  
  391.     // If there's a tag that needs fields added, try
  392.     // to parse the fields out of this source line.
  393.  
  394.     if(fAllowPending)
  395.         CheckCurParse(in, log, fmt);
  396. }
  397.  
  398.  
  399.  
  400.  
  401.  
  402. /*-----------------------------------------------------------------------
  403.     @doc EXTERNAL EXTRACT
  404.         
  405.     @func char *| GetBeginComment | Looks for the beginning of a comment
  406.         block within the given line. 
  407.             
  408.     @rdesc Returns zero if no comment found, or one of the following
  409.         values to indicate the type of comment found:
  410.  
  411.     @flag commentSemicolon | Uses semicolons (assembler).
  412.     @flag commentSlashstar | Uses forward slash character followed 
  413.         by asterisk character.
  414.     @flag commentApostrophe | Uses apostrophe (Basic-style).
  415.     @flag commentSlashslash | Uses two forward slash characters.
  416.         
  417. */
  418.  
  419. int GetBeginComment(
  420.     const char *sz,         // @parm Line to search for comment block
  421.     int nSourceType,        // @parm Language type:
  422.                             //  @flag sourceC | C or C++ (/* or // comments)
  423.                             //  @flag sourceAsm | Assembly file (; comments)
  424.                             //  @flag sourceBas | Basic file (' comments)
  425.     char chCommentDelim,    // Generic comment delim type for non-C++ files
  426.     const char *&szBlock,   // @parm Filled in with a pointer to the first
  427.                             //  character of the comment delimiter.
  428.     const char *&szText)   // @parm Filled with the first text character in
  429.                             //  in the comment.
  430. {
  431.     int bLit;
  432.  
  433.     ASSERT(sz);
  434.     
  435.     sz = EatWhite(sz);
  436.     
  437.     switch(nSourceType)
  438.     {
  439.     case sourceC:
  440.         
  441.         // Look for a slash-slash or slash-asterisk
  442.         
  443.         for(bLit = FALSE; *sz; sz++)
  444.         {
  445.             if(*sz == chBackslash)
  446.             {
  447.                 // keep going past escape character
  448.             }
  449.             else if(*sz == chDoubleQuote || *sz == chSingleQuote)
  450.             {
  451.                 // quotation mark - literal string
  452.  
  453.                 bLit = !bLit;
  454.             }
  455.             else if(bLit)
  456.             {
  457.                 // keep going past literal string
  458.             }
  459.             else if(*sz == chSlash)
  460.             {
  461.                 szBlock = sz;
  462.  
  463.                 if(*(sz+1) == chAsterisk)
  464.                 {
  465.                     szText = EatNoiseChars(sz+2);
  466.  
  467.                     return commentSlashstar;
  468.                 }
  469.                 else if(*(sz+1) == chSlash)
  470.                 {
  471.                     while(*sz == chSlash)
  472.                         sz++;
  473.  
  474.                     szText = EatNoiseChars(sz);
  475.  
  476.                     return commentSlashslash;
  477.                 }
  478.             }
  479.         }
  480.         break;
  481.         
  482.     case sourceAsm:
  483.     case sourceBas:
  484.     case sourceGeneric:
  485.         
  486.         for(bLit = FALSE; *sz; sz++)
  487.         {
  488.             if(*sz == chDoubleQuote)
  489.             {
  490.                 bLit = !bLit;
  491.             }
  492.             else if(bLit)
  493.             {
  494.                 // keep going
  495.             }
  496.             else if(*sz == chCommentDelim)
  497.             {
  498.                 szBlock = sz;
  499.                 szText = EatNoiseChars(sz+1);
  500.  
  501.                 return commentSingleChar;
  502.             }
  503.         }
  504.         break;
  505.     }
  506.     
  507.     return 0;
  508. }
  509.  
  510. /*-----------------------------------------------------------------------
  511.     @doc EXTERNAL EXTRACT
  512.         
  513.     @func int | IsEndComment | Determines if the given line ends a
  514.         comment block.
  515.             
  516.     @parm char * | ptr | Specifies a pointer to a null-terminated buffer
  517.         containing the line.
  518.     @parm int | in.m_nCommentType | Specifies a constant identifying the type 
  519.         of comment used in the block:
  520.         @flag commentSingleChar | Uses single character (basic, assembler, etc.)
  521.         @flag commentSlashstar | Uses forward slash character followed 
  522.             by asterisk character.
  523.         @flag commentSlashslash | Uses two forward slash characters.
  524.             
  525.     @rdesc The return value is TRUE if the line ends a comment block,
  526.         otherwise it is FALSE.
  527. */
  528. int IsEndComment(const char *sz, int nCommentType)
  529. {
  530.     switch(nCommentType)
  531.     {
  532.     case commentSlashstar:
  533.         for(sz = EatWhite(sz); *sz; sz++)
  534.         {
  535.             if ((*sz == '*') && (*(sz+1) == '/'))
  536.                 return TRUE;
  537.         }
  538.         break;
  539.  
  540.     case commentSingleChar:
  541.     case commentSlashslash:
  542.     default:
  543.         return TRUE;
  544.     }
  545.     return FALSE;
  546. }
  547.  
  548. /*-----------------------------------------------------------------------
  549.     @doc EXTERNAL EXTRACT
  550.         
  551.     @func int | IsBeginTopic | Determines if the given line begins
  552.         an autoduck topic.
  553.             
  554.     @parm char * | szLine | Specifies a pointer to a null-terminated buffer
  555.         containing the line.
  556.     @parm PTOPICTAG | pTopicTags | Specifies a list of valid topic tags 
  557.         such as "func", "api", "asm", and "msg".
  558.  
  559.     @rdesc The return value is TRUE if the line begins an autoduck topic,
  560.         otherwise it is FALSE.
  561. */
  562. int IsBeginTopic(
  563.     const char *szTag, 
  564.     FormatInfo &fmt)
  565. {
  566.     // Test for case where no tag found
  567.     if (*szTag == '\0')
  568.         return FALSE;
  569.     
  570.     CFmtSrchTopic srch;
  571.     srch.m_sName = szTag;
  572.  
  573.     return (fmt.topic.Get(&srch) != NULL);
  574. }
  575.  
  576.  
  577.  
  578. /*-----------------------------------------------------------------------
  579.     @doc EXTERNAL EXTRACT
  580.         
  581.     @func char * | EatNoiseChars | Strips noise characters from the given
  582.         line. The following character combinations are considered to
  583.         be noise characters:
  584.             
  585.         <em-> Semicolon if first character in line
  586.             
  587.         <em-> Apostrophe if first character in line
  588.             
  589.         <em-> Consecutive "border" characters beginning with first 
  590.               character in line ("***************") or second character
  591.               if first character is space (" **************")
  592.             
  593.         <em-> All characters following and including end comment characters
  594.             
  595.         <em-> All text following and including internal designator
  596.               ( ; I n t e r n a l )
  597.  
  598.         <em-> Whitespace, *, \<, and // characters preceding internal 
  599.               designator
  600.                   
  601.         <em-> // comment characters
  602.             
  603.     @parm char * | szLine | Specifies a pointer to a 
  604.         null-terminated buffer containing the line to process.
  605.             
  606.     @rdesc Returns a pointer to the first non-noise character.
  607.     
  608.     @comm This function is called by <f ExtractTopic> before a source
  609.         line is added to the extract buffer. It assumes that each line 
  610.         ends in newline/null-terminator characters.
  611.         
  612.     @xref <f StripLeadingWhiteSpace>
  613. */
  614. const char *EatNoiseChars(const char *szLine)
  615. {
  616.     const char *szT;
  617.  
  618.     ASSERT(szLine);
  619.     
  620.     // Check for empty string
  621.     switch(*szLine)
  622.     {
  623.     case '\0':
  624.     case ';':
  625.     case '\'':
  626.         break;
  627.         
  628.     default:
  629.  
  630.         const char *szStart = szLine;
  631.  
  632.         szT = EatWhite(szLine);
  633.         if(isnoise(*szT))
  634.             szLine = szT;
  635.            
  636.         while(isnoise(*szLine))
  637.             ++szLine;
  638.  
  639.         // check special cases: end of comment block, want to 
  640.         // preserve to recognize
  641.  
  642.         if(*szLine == '/' && *(szLine-1) == '*')
  643.             szLine--;
  644.  
  645.         // to remove the leading noise characters, they must be followed
  646.         // by a whitespace character or by a carriage return.
  647.  
  648.         else if(!(iswhite(*szLine) || *szLine == '\r' || *szLine == '\n'))
  649.             szLine = szStart;        
  650.  
  651.         break;
  652.     }    
  653.     return szLine;
  654. }
  655.  
  656.  
  657. /*
  658. @func Adds the current line to the extracted topic buffer.
  659. */
  660. int AddLineToBuffer(
  661.     const char *szLine,      //@parm Line to add.
  662.     CSrcInput &in)          //@parm Input state structure.
  663. {
  664.     size_t nLen;
  665.     BOOL bStripComment = FALSE;
  666.  
  667.     // Get the line length.
  668.  
  669.     nLen = strlen(szLine);
  670.  
  671.     // Empty, return.
  672.     if(nLen == 0)
  673.         return 0;
  674.  
  675.     // Back up over any trailing spaces.
  676.  
  677.     const char *szEnd = TrimWhite(szLine+nLen, szLine);
  678.  
  679.     nLen = isspace(*szEnd) ? 0 : szEnd-szLine+1;
  680.  
  681.     // Back up over the comment delimiter, if present.
  682.  
  683.     if( (in.m_nState.EndComment && in.m_nCommentType == commentSlashstar) &&
  684.         (nLen >= 2 && szLine[nLen-1] == chSlash && szLine[nLen-2] == chAsterisk) )
  685.     {
  686.         if(nLen == 2)
  687.         {
  688.             nLen = 0;
  689.         }
  690.         else
  691.         {
  692.             szEnd = TrimWhite(&szLine[nLen-2], szLine);
  693.         
  694.             nLen = isspace(*szEnd) ? 0 : szEnd-szLine+1;
  695.         }
  696.     }
  697.    
  698.     // Make sure we don't overwrite the input buffer.
  699.  
  700.     if((int)((in.m_szTagCur - in.m_achTagBuf) + nLen + 1) >= TAGBUFSIZE)
  701.         return errTagTooBig;
  702.  
  703.     if(nLen)
  704.         memcpy(in.m_szTagCur, szLine, nLen);
  705.  
  706.     in.m_szTagCur[nLen++] = '\n';
  707.  
  708.     in.m_szTagCur += nLen;
  709.  
  710.     return 0;
  711. }
  712.  
  713. /*
  714. @func Gets the Autoduck tag from the given line.
  715.     
  716. @rdesc Returns the index of the next character following the end
  717.     of the tag.
  718.         
  719. @comm The given line can contain leading whitespace and noise
  720.     characters.
  721. */
  722. const char *GetTag(
  723.     CString &sTagDest,        //@parm Destination buffer for tag name.
  724.     const char *szSrc)        //@parm Line to extract tag from.
  725. {
  726.     int i;
  727.  
  728.     if(!istagchar(*szSrc))
  729.         return szSrc;
  730.  
  731.     // Copy tag.
  732.  
  733.     for(i = 0; szSrc[i] && istagchar(szSrc[i]) && i < MAXTAGSIZE; i++);
  734.  
  735.     sTagDest = CString(szSrc, i);
  736.  
  737.     return szSrc + i;
  738. }
  739.  
  740.  
  741. /*
  742. @func This function checks extraction flags associated with a paragraph
  743. tag. Tag-level extraction flags allow an Autoduck author to exclude or 
  744. include specific paragraphs within a topic.
  745.  
  746. @rdesc Returns NULL if the extraction flags were not specified on the
  747. command line, or if there was a syntax error in the extraction specifier.
  748. Otherwise, returns a pointer to the text following the extraction specifier.
  749. */
  750.  
  751. int CheckTagExclusion(
  752.     const char *&szText,    //@parm Text to check eg. @tag:(JAPANESE) - updated with
  753.                             // character position following extraction text
  754.     CExprToken &expr,       //@parm Extraction tokens from command line
  755.     BOOL &bExcludeTag,      //@parm Whether to exclude the tag from extraction
  756.     CString *psLocalDocTag = NULL)    //@parm String to fill with topic-level doc tag. String
  757.                             // is cleared if no doc tag exists for topic.
  758. {
  759.     bExcludeTag = FALSE;
  760.     
  761.     // Get the colon.
  762.  
  763.     szText = EatWhite(szText);
  764.     if(*szText != chColon)
  765.     {
  766.         if(psLocalDocTag)
  767.             psLocalDocTag->Empty();
  768.         return 0;
  769.     }
  770.     
  771.     // Get the opening paren.
  772.  
  773.     szText = EatWhite(++szText);
  774.     
  775.     if(*szText != chOpenParen)
  776.         return warnTagExclSyntax;
  777.  
  778.     // Get the closing paren.
  779.  
  780.     szText = EatWhite(++szText);
  781.  
  782.     const char *szFlags = szText;
  783.     static char szTagStopChar[] = ")\n|";
  784.  
  785.     szText = SeekEnd(szText, szTagStopChar, MAXLINEBUFSIZE);
  786.  
  787.     if(szText == NULL || *szText != chCloseParen)
  788.         return warnTagExclSyntax;
  789.  
  790.     // Check the doc IDs.
  791.  
  792.     CString sTagTokensRaw(szFlags, szText-szFlags);
  793.     CString sTagTokens;
  794.  
  795.     CopyFlags(sTagTokens, sTagTokensRaw);
  796.  
  797.     bExcludeTag = !expr.Eval(sTagTokens);
  798.  
  799.     if(psLocalDocTag)
  800.         *psLocalDocTag = sTagTokens;
  801.  
  802.     // Past the closing paren before returning.
  803.  
  804.     ++szText;
  805.  
  806.     return 0;
  807. }
  808.  
  809. /****************************************************************************/
  810.  
  811.  
  812. //@func Walks the argument list and processes each input
  813. // specified. Expands wilcards into multiple input file
  814. // arguments.
  815.  
  816. void ExtractTopics(
  817.     CFile &out,
  818.     RUNOPTIONS &run,
  819.     CTopicLog  &log,
  820.     FormatInfo &fmt)
  821. {
  822.        char szPath[_MAX_PATH];
  823.     char szInFile[_MAX_PATH];
  824.  
  825.        char szDrive[_MAX_DRIVE];
  826.        char szDir[_MAX_DIR];
  827.  
  828. #if defined(_DOS)
  829.     _find_t fileInfo;
  830. #else
  831.     _finddata_t fileInfo;
  832.     long hFile;
  833. #endif
  834.  
  835.     int i, nFiles;
  836.     int nInput = 0;
  837.  
  838.     nFiles = run.asInputFiles.GetSize();
  839.     
  840.     for(i = 0; i < nFiles; i++)
  841.     {
  842.         ASSERT(run.asInputFiles[i]);
  843.  
  844.         strcpy(szInFile, *run.asInputFiles[i]);
  845.  
  846. #if defined(_DOS)
  847.         if(_dos_findfirst(szInFile, _A_NORMAL, &fileInfo))
  848. #else
  849.         if((hFile = _findfirst(szInFile, &fileInfo)) == -1L)
  850. #endif
  851.         {
  852.             PrintError(szInFile, NO_LINE, errfileFileNotFound);
  853.             continue;
  854.         }
  855.  
  856.         // Build the pathname stub for the input filename.
  857.  
  858.         _splitpath( szInFile, szDrive, szDir, NULL, NULL);
  859.         *szPath = '\0';
  860.         strcat(szPath, szDrive);
  861.         strcat(szPath, szDir);
  862.  
  863.         // Process each matching file in wildcard 
  864.         do
  865.         {
  866.             sprintf(szInFile, "%s%s", szPath, fileInfo.name);
  867.  
  868. #if !defined(_DOS)
  869.             if(fileInfo.attrib & _A_SUBDIR)
  870.                 continue;
  871. #endif
  872.             ExtractFile(out, szInFile, log, run, fmt);
  873.  
  874. #if defined(_DOS)
  875.         } while(_dos_findnext(&fileInfo) == 0);
  876. #else
  877.         } while(_findnext(hFile, &fileInfo) == 0);
  878. #endif
  879.     }
  880. }
  881.  
  882.  
  883. //@func Adds a topic to the log.
  884.  
  885. //@rdesc Returns zero on success or an error code.
  886. // If an error code, the topic (and tag list) is
  887. // deleted.
  888.  
  889. void AddTopic(
  890.     CTopicLog &log,         //@parm Topic log.
  891.     CTagList &listTags,     //@parm Linked list of topic tags.
  892.     CSrcInput &in,            //@parm Input file information
  893.     CFile &out,             //@parm Temp file to output topic text.
  894.     FormatInfo &fmt,        //@parm Format information.
  895.     RUNOPTIONS &run)        //@parm Runtime options (verbosity)
  896. {
  897.     CTopic *pTopic;
  898.     CFmtSrchTopic srch;
  899.     CFmtTopic *pfmtTopic;
  900.     char szNameBuf[MAXTOPICNAMELEN+1];
  901.     CTag *ptagHead = listTags.GetHead();
  902.  
  903.     // Create a topic to hold the tag list.
  904.  
  905.     pTopic = new CTopic;
  906.     pTopic->SetTags(listTags);
  907.  
  908.     // See if valid topic tag.
  909.  
  910.     srch.m_sName = ptagHead->sTag;
  911.     pfmtTopic = (CFmtTopic *)fmt.topic.Get(&srch);
  912.     
  913.     if(NULL == pfmtTopic)
  914.     {
  915.         PrintError(log.GetSrcFn(in.m_nFile), ptagHead->lSrcLineNum, warnUnknownTopicTag);
  916.         return;
  917.     }
  918.  
  919.     // Check field count.
  920.  
  921.     if(pfmtTopic->GetNumFields() != ptagHead->nFields)
  922.         PrintError(log.GetSrcFn(in.m_nFile), ptagHead->lSrcLineNum, warnWrongNumFields);
  923.     
  924.     // Print name and context strings into the topic object.
  925.  
  926.     pfmtTopic->PrintName(szNameBuf, ptagHead);
  927.  
  928.     if(run.nOptions.Verbose)
  929.         printf("\t%s\n", szNameBuf);
  930.  
  931.     pTopic->SetName(szNameBuf);
  932.  
  933.     pfmtTopic->PrintContext(szNameBuf, ptagHead);
  934.  
  935.     pTopic->SetContext(szNameBuf);
  936.  
  937.     // Set the other fields.
  938.  
  939.     pTopic->SetWeight(pfmtTopic->GetSortWeight());
  940.     pTopic->SetSrcFn(in.m_nFile);
  941.     pTopic->SetSrcLine(ptagHead->lSrcLineNum);
  942.     pTopic->SetDocTag(log.GetDocTagIndex());
  943.  
  944.     if(in.m_sLocalDocTag.GetLength() != 0)
  945.         pTopic->SetLocalDocTag(in.m_sLocalDocTag);
  946.  
  947.     pTopic->Write(out);
  948.  
  949.     log.Add( pTopic, CTopicLog::AddGenerateUnique );
  950.  
  951.     listTags.RemoveAll();
  952. }
  953.  
  954. /****************************************************************************/
  955.  
  956. //@func Extracts topics from an input file.
  957.  
  958. void ExtractFile(
  959.     CFile &out,                 //@parm Temp file to output tag text
  960.     const char *szFilename,     //@parm Input file to process
  961.     CTopicLog  &log,            //@parm Topic log, gets new topics.
  962.     RUNOPTIONS &run,            //@parm Runtime options.
  963.     FormatInfo &fmt)            //@parm Formatting/tag information.
  964. {
  965.     CSrcInput in;
  966.     CFileException except;
  967.     int nTopic = 0;
  968.  
  969. TRY
  970. {
  971.     if(run.nOptions.Verbose)
  972.         fprintf(stdout, "%s\n", szFilename);
  973.  
  974.     // Open file, record filename in topic log.
  975.  
  976.     if(!in.Open(szFilename, CFile::modeRead | CFile::typeBinary, &except))
  977.     {
  978.         PrintError(szFilename, NO_LINE, except.m_cause + 100);
  979.         return;
  980.     }
  981.     
  982.     in.m_nFile = log.AddSrcFn(szFilename);
  983.  
  984.     // Determine source type. Default to "C" if unknown.
  985.  
  986.     CFmtSrchExt srchExt;
  987.     char szExt[_MAX_EXT+1];
  988.     
  989.     _splitpath(szFilename, NULL, NULL, NULL, szExt);
  990.     srchExt.m_sExt = &szExt[1];
  991.  
  992.     CFmtExt *pExt = (CFmtExt *)fmt.extension.Get(&srchExt);
  993.     if(NULL == pExt)
  994.     {
  995.         PrintError(szFilename, NO_LINE, warnFileTypeUnknown);
  996.         in.m_nSourceType = sourceC;
  997.     }
  998.     else
  999.     {
  1000.         in.m_nSourceType = pExt->GetSourceType();
  1001.     }
  1002.  
  1003.     in.m_chCommentDelim = pExt->GetGenericDelim();
  1004.   
  1005.     // Set the doc tag number. This might be -1 if no doc tag has been
  1006.     // found in the file yet - it will be reset later within this function.
  1007.  
  1008.     while (ExtractTopic(in, log, run, fmt, out))
  1009.     {
  1010.         // Create a topic to hold the tag list.
  1011.  
  1012.         AddTopic(log, in.m_listTags, in, out, fmt, run);
  1013.  
  1014.         // If an auxiliary topic left over, add it to the list also.
  1015.  
  1016.         if(!in.m_listAuxTags.IsEmpty())
  1017.             AddTopic(log, in.m_listAuxTags, in, out, fmt, run);
  1018.     };
  1019.  
  1020.     in.Close();
  1021.  
  1022.     return;
  1023. }
  1024. CATCH(CFileException, e)
  1025. {
  1026.     PrintError(szFilename, in.m_lCurLine, e->m_cause + 100);
  1027.     return;
  1028. }
  1029. AND_CATCH(CMemoryException, e)
  1030. {
  1031.     PrintError(szFilename, in.m_lCurLine, errMemory);
  1032.     return;
  1033. }
  1034. END_CATCH
  1035. }
  1036.  
  1037.  
  1038. /*
  1039. @func This function seeks the start of an Autoduck topic in the
  1040. input stream, and once it finds one extracts all the topic
  1041. text. 
  1042.  
  1043. A topic can span multiple comment lines. Topic text can
  1044. consist of commented text as well as information read from
  1045. source language statements.
  1046.  
  1047. The function constructs a linked list of TAG structures
  1048. describing each tag within the topic.
  1049.  
  1050. @rdesc Pointer to head of linked <t TAG> list or NULL no topic
  1051. was extracted.
  1052. */
  1053.  
  1054. BOOL ExtractTopic(
  1055.     CSrcInput    &in,       //@parm Source file
  1056.     CTopicLog    &log,      //@parm Topic log, for updates to list
  1057.                             // of extract tags and for adding auxiliary
  1058.                             // topics.
  1059.     RUNOPTIONS   &run,      //@parm Runtime options structure
  1060.     FormatInfo   &fmt,      //@parm Format entries
  1061.     CFile &out)                //@parm Output temp file - for aux topics
  1062. {
  1063.     CTag *    ptagHead = NULL;
  1064.     
  1065.     char *szComment;                // Start of comment block (at // etc.)
  1066.     const char *szCommentText;      // Start of comment text
  1067.     const char *szTag;
  1068.     const char *szT;
  1069.     int  nRet;
  1070.     BOOL bExcludeTag;
  1071.  
  1072.     CString sTag;
  1073.  
  1074.     int fTopicTag = TRUE;
  1075.  
  1076.     in.m_listTags.RemoveAll();
  1077.  
  1078.     in.m_pendMain.RemoveAll();
  1079.     in.m_pendAux.RemoveAll();
  1080.     in.m_listAuxTags.RemoveAll();
  1081.  
  1082.     // Initialize input state - leave the state variables as is because they
  1083.     // indicate what's up from the last topic extracted. State variables are
  1084.     // updated within this function before exit.
  1085.  
  1086.     in.m_achTagBuf[0] = '\0';
  1087.  
  1088.     in.m_nState.HaveTopicTag = FALSE;
  1089.     in.m_nState.Tag = FALSE;
  1090.     in.m_nState.GrowingTag = FALSE;
  1091.     in.m_nState.Topic = FALSE;
  1092.     in.m_nState.SkipTopic = FALSE;
  1093.     in.m_nState.AuxTopicTag = FALSE;
  1094.     in.m_nState.AuxTopicOpen = FALSE;
  1095.     in.m_nState.AuxPara = FALSE;
  1096.     in.m_nState.AuxParaTag = FALSE;
  1097.  
  1098.     in.m_listAuxTags.RemoveAll();
  1099.     in.m_sTagAux.Empty();
  1100.  
  1101.     // Set current doc tag - may be -1 if none has been found in
  1102.     // topic yet.
  1103.  
  1104.     int nDocTag = log.GetDocTagIndex();
  1105.  
  1106.     // Loop kicks out at the end of the topic, which can be any of the
  1107.     // following situations:
  1108.     // * End of file
  1109.     // * A new topic is found
  1110.     // * A "@doc" tag is found, signifying end of topic
  1111.     
  1112.     while( ! in.EndOfFile() )       // while not EOF
  1113.     {
  1114.         // Get next line from source.
  1115.  
  1116.         if(nRet = in.GetLine())
  1117.         {
  1118.             PrintError(log.GetSrcFn(), in.m_lCurLine, nRet);
  1119.             return NULL;
  1120.         }
  1121.  
  1122.         // ********* DETERMINE IF COMMENT LINE **************************
  1123.  
  1124.         // If the last line ended a comment, assume this isn't a comment
  1125.         // now.
  1126.         
  1127.         if(in.m_nState.EndComment)
  1128.         {
  1129.             in.m_nState.Comment = FALSE;
  1130.             in.m_nState.EndComment = FALSE;
  1131.  
  1132.              // End of a slash-star block ends the tag.
  1133.              
  1134.              if(in.m_nCommentType == commentSlashstar)
  1135.                  in.m_nState.GrowingTag = FALSE;
  1136.         }
  1137.  
  1138.         if(in.m_nState.Comment)
  1139.         {
  1140.             in.m_nState.BeginComment = FALSE;
  1141.  
  1142.             // Comment starts at beginning of line.
  1143.             
  1144.             szComment = in.m_szLineBuf;
  1145.  
  1146.             // If this is a /* block, and the current line begins with a //
  1147.             // comment, ignore the line.
  1148.         
  1149.             if(in.m_nCommentType == commentSlashstar &&
  1150.                strncmp(szComment, "//", 2) == 0)
  1151.             {
  1152.                 continue;
  1153.             }
  1154.  
  1155.             // Advance past noise characters
  1156.  
  1157.             szCommentText = EatNoiseChars(szComment);
  1158.         }
  1159.         else    // not in comment block
  1160.         {
  1161.             // Skip blank lines, also end any single-line comment block
  1162.             // (blank line following marks the end of the block).
  1163.  
  1164.             szT = EatWhite(in.m_szLineBuf);
  1165.  
  1166.             if(*szT == '\0' || *szT == '\n')
  1167.             {
  1168.                 in.m_nState.GrowingTag = FALSE;
  1169.                 continue;
  1170.             }
  1171.  
  1172.             // See if a comment starts on this line. 
  1173.  
  1174.             in.m_nCommentType = GetBeginComment(in.m_szLineBuf, 
  1175.                     in.m_nSourceType, in.m_chCommentDelim, szComment, szCommentText);
  1176.  
  1177.             if(in.m_nCommentType)
  1178.             {
  1179.                 in.m_nState.Comment = TRUE;
  1180.                 in.m_nState.BeginComment = TRUE;
  1181.  
  1182.                 // See if any source text precedes the comment, if so see if 
  1183.                 // any tags are pending for parsing.
  1184.  
  1185.                 szT = EatWhite(in.m_szLineBuf);
  1186.                 if(szT != szComment)
  1187.                     CheckCurParse(in, log, fmt);
  1188.             }
  1189.             else
  1190.             {
  1191.                 // If no comment starts on this line, this is a non-blank,
  1192.                 // non-comment line, a candidate for parsing any pending
  1193.                 // tag.
  1194.  
  1195.                 //CheckCurParse(in, log, fmt);//tag
  1196.                 CheckCurTag(in, log, out, fmt, run, TRUE); 
  1197.  
  1198.                 continue;
  1199.             }
  1200.         }
  1201.  
  1202.  
  1203.         // See if this line ends the comment. Even if it does, we
  1204.         // might still want to store the line with the topic.
  1205.  
  1206.         if(IsEndComment(szCommentText, in.m_nCommentType))
  1207.         {
  1208.             in.m_nState.EndComment = TRUE;
  1209.         }
  1210.  
  1211.         // Get the tag starting the line, if present.
  1212.         
  1213.         sTag.Empty();
  1214.         in.m_nState.AuxParaTag = FALSE;
  1215.         szTag = EatWhite(szCommentText);
  1216.         
  1217.         if(chAtSign == *szTag)
  1218.         {
  1219.             // Auxiliary paragraph tag
  1220.  
  1221.             if(chAtSign == szTag[1])
  1222.             {
  1223.                 szTag++;
  1224.                 if(in.m_nState.Topic == TRUE)
  1225.                     in.m_nState.AuxParaTag = TRUE;
  1226.             }
  1227.  
  1228.             // Get the tag.
  1229.  
  1230.             szCommentText = GetTag(sTag, szTag+1);
  1231.         }
  1232.  
  1233.         // If we're not in an autoduck block, see if the current line
  1234.         // starts an autoduck block.
  1235.  
  1236.         if(!in.m_nState.Autoduck)
  1237.         {
  1238.             if(_stricmp(sTag, "doc") == 0)
  1239.             {
  1240.                 CopyFlags(in.m_sDocFlags, szCommentText);
  1241.  
  1242.                 if(run.exprExtract.Eval(in.m_sDocFlags))
  1243.                 {
  1244.                     in.m_nState.Autoduck = TRUE;
  1245.  
  1246.                     nDocTag = log.AddDocTag(in.m_sDocFlags);
  1247.                 }
  1248.             }
  1249.             continue;
  1250.         }
  1251.  
  1252.         // ******* WITHIN A TOPIC *******************************
  1253.         
  1254.         if(in.m_nState.Topic)
  1255.         {
  1256.             // Skip line if topic tag was excluded
  1257.             if(in.m_nState.SkipTopic)
  1258.                 continue;
  1259.  
  1260.             // If the line is an @doc tag, end the current topic and tag, and
  1261.             // pop out of the Autoduck state. Will reset on next call.
  1262.  
  1263.             if(_stricmp(sTag, "doc") == 0)
  1264.             {
  1265.                 // Check the current tag.
  1266.  
  1267.                 CheckCurTag(in, log, out, fmt, run, TRUE); //FALSE);
  1268.  
  1269.                 // Store doc tag line to be used on next call to ExtractTopic
  1270.  
  1271.                 in.SetReuse();
  1272.                 
  1273.                 in.m_nState.Autoduck = FALSE;
  1274.  
  1275.                 if(in.m_nState.BeginComment)
  1276.                 {
  1277.                     in.m_nState.BeginComment = FALSE;
  1278.                     in.m_nState.Comment = FALSE;
  1279.                 }
  1280.  
  1281.                 return TRUE;
  1282.             }
  1283.             
  1284.             // New topic found. End current topic.
  1285.             
  1286.             if(IsBeginTopic(sTag, fmt))
  1287.             {
  1288.                 // See if there's a field search pending. Since we're
  1289.                 // still in a comment block, issue a warning message
  1290.                 // and cancel pending search.
  1291.  
  1292.                 CheckCurTag(in, log, out, fmt, run, TRUE);//FALSE);
  1293.  
  1294.                 // Store first line of topic for next call to ExtractTopic
  1295.  
  1296.                 in.SetReuse();
  1297.                 
  1298.                 if(in.m_nState.BeginComment)
  1299.                 {
  1300.                     in.m_nState.BeginComment = FALSE;
  1301.                     in.m_nState.Comment = FALSE;
  1302.                 }
  1303.  
  1304.                 return TRUE;
  1305.             }
  1306.  
  1307.             // New tag found. End current tag.
  1308.  
  1309.             if( !sTag.IsEmpty() )
  1310.             {
  1311.                 // See if this tag has extraction flags. If it does,
  1312.                 // make sure the tag is included in the build.
  1313.  
  1314.                 nRet = CheckTagExclusion(szCommentText, run.exprExtract, bExcludeTag);
  1315.                 if(nRet)
  1316.                     PrintError(log.GetSrcFn(), in.m_lCurLine, nRet);
  1317.                 
  1318.                 // New tag. See if there's a new topic defined on this line.
  1319.                 // If so, set the aux topic open flag variables to notify
  1320.                 // <f CheckCurTag>. If another aux topic is pending now, need
  1321.                 // to add it to the log and clear the aux tag list.
  1322.  
  1323.                 in.m_nState.AuxTopicTag = (chComma == *szCommentText);
  1324.  
  1325.                 // Now check the current tag state = this means closing out the
  1326.                 // preceding tag and possibly adding the current aux
  1327.                 // topic to the log.
  1328.  
  1329.                 CheckCurTag(in, log, out, fmt, run, FALSE);
  1330.  
  1331.                 // If tag was excluded just break out here.
  1332.  
  1333.                 if(bExcludeTag)
  1334.                 {
  1335.                     in.m_nState.Tag = FALSE;
  1336.                     in.m_nState.GrowingTag = FALSE;
  1337.  
  1338.                     continue;
  1339.                 }
  1340.                
  1341.                 // If auxiliary tag, get the name of the aux topic tag attached
  1342.                 // to this paragraph.
  1343.  
  1344.                 if(in.m_nState.AuxTopicTag)
  1345.                 {
  1346.                     in.m_sTagAux.Empty();
  1347.                     szCommentText = GetTag(in.m_sTagAux, szCommentText+1);
  1348.                     
  1349.                     in.m_nState.AuxTopicOpen = (szTag != szCommentText+1);
  1350.                 }
  1351.  
  1352.                 // Store the paragraph tag name and any text preceding
  1353.                 // the tag comment block. May need the text for source
  1354.                 // parsing.
  1355.  
  1356.                 in.m_sTag = sTag;
  1357.  
  1358.                 if(in.m_nState.BeginComment)
  1359.                 {
  1360.                     memcpy(in.m_achCommentStart, in.m_szLineBuf, 
  1361.                             szComment - in.m_szLineBuf);
  1362.                     in.m_achCommentStart[szComment-in.m_szLineBuf] = '\0';
  1363.                 }
  1364.                 else
  1365.                 {
  1366.                     in.m_achCommentStart[0] = '\0';
  1367.                 }
  1368.  
  1369.                 // Reset tag pointer and tag buf.
  1370.  
  1371.                 ZeroMem(in.m_achTagBuf, TAGBUFSIZE+1);
  1372.                 in.m_szTagCur = in.m_achTagBuf;
  1373.                 
  1374.                 in.m_nLineTag = in.m_lCurLine;
  1375.                 in.m_nState.Tag = TRUE;
  1376.                 in.m_nState.GrowingTag = TRUE;
  1377.             }
  1378.  
  1379.             // Add the line to the tag buffer
  1380.  
  1381.             if(in.m_nState.GrowingTag)
  1382.             {
  1383.                 nRet = AddLineToBuffer(szCommentText, in);
  1384.                 if(nRet)
  1385.                     PrintError(log.GetSrcFn(), in.m_lCurLine, nRet);
  1386.             }
  1387.  
  1388.             // Nothing in comment of interest - so see if 
  1389.             // source text preceding the comment is parsable.
  1390.  
  1391.             if(in.m_nState.BeginComment && 
  1392.                (!in.m_pendMain.IsEmpty() || !in.m_pendAux.IsEmpty()) )
  1393.             {
  1394.                 *szComment = '\0';
  1395.                 const char *szSrcText = EatWhite(in.m_szLineBuf);
  1396.                 if(*szSrcText)
  1397.                     CheckCurParse(in, log, fmt);
  1398.             }
  1399.         } // end IN TOPIC
  1400.        
  1401.  
  1402.         else    // NOT IN TOPIC
  1403.         {
  1404.             // Does this line begin a topic? If so, we will add the line 
  1405.             // to the buffer.
  1406.             
  1407.             if(IsBeginTopic(sTag, fmt))
  1408.             {
  1409.                 // See if topic has extraction flags. If it does, exclude the
  1410.                 // whole topic.
  1411.  
  1412.                 nRet = CheckTagExclusion(szCommentText, run.exprExtract, bExcludeTag, &in.m_sLocalDocTag);
  1413.                 if(nRet)
  1414.                     PrintError(log.GetSrcFn(), in.m_lCurLine, nRet);
  1415.                 
  1416.                 
  1417.                 if(bExcludeTag)
  1418.                 {
  1419.                     in.m_nState.SkipTopic = TRUE;                    
  1420.  
  1421.                     continue;
  1422.                 }
  1423.                 else
  1424.                 {
  1425.                     in.m_nState.SkipTopic = FALSE;
  1426.                 }
  1427.  
  1428.                 in.m_nState.Topic = TRUE;
  1429.                 in.m_nState.Tag = TRUE;
  1430.                 in.m_nState.GrowingTag = TRUE;
  1431.                 in.m_nState.HaveTopicTag = FALSE;
  1432.  
  1433.                 in.m_nLineTag = in.m_lCurLine;
  1434.  
  1435.                 in.m_sTag = sTag;
  1436.                 
  1437.                 // Eat white space following tag
  1438.  
  1439.                 szCommentText = EatWhite(szCommentText);
  1440.                 
  1441.                 // Reset pending data
  1442.                 
  1443.                 in.EmptyParse();
  1444.  
  1445.                 // Reset tag pointer and tag buf.
  1446.                 ZeroMem(in.m_achTagBuf, TAGBUFSIZE+1);
  1447.                 in.m_szTagCur = in.m_achTagBuf;
  1448.                 in.m_nLineTag = in.m_lCurLine;
  1449.                 
  1450.                 nRet = AddLineToBuffer(szCommentText, in);
  1451.                 if(nRet)
  1452.                     PrintError(log.GetSrcFn(), in.m_lCurLine, nRet);
  1453.  
  1454.                 // If this line starts a new tag, store the text preceding
  1455.                 // the comment.
  1456.  
  1457.                 if(in.m_nState.BeginComment)
  1458.                 {
  1459.                     memcpy(in.m_achCommentStart, in.m_szLineBuf, 
  1460.                             szComment - in.m_szLineBuf);
  1461.                     in.m_achCommentStart[szComment-in.m_szLineBuf] = '\0';
  1462.                 }
  1463.                 else
  1464.                 {
  1465.                     in.m_achCommentStart[0] = '\0';
  1466.                 }
  1467.             }
  1468.             
  1469.             // Check for @doc tag.
  1470.             
  1471.             else if(_stricmp(sTag, "doc") == 0)
  1472.             {
  1473.                 // Pop out of Autoduck state.
  1474.  
  1475.                 in.m_nState.Autoduck = FALSE;
  1476.  
  1477.                 // Store doc tag line to be used on next call to ExtractTopic
  1478.  
  1479.                 in.SetReuse();
  1480.             }
  1481.             
  1482.             // Continue looking. Print a warning if the line contains
  1483.             // a paragraph tag.
  1484.             
  1485.             else if(!sTag.IsEmpty() && !in.m_nState.SkipTopic)
  1486.             {
  1487.                 PrintError(log.GetSrcFn(), in.m_lCurLine, warnWantTopicTag);
  1488.             }
  1489.  
  1490.         }   // end NOT IN TOPIC
  1491.  
  1492.     }   // while loop
  1493.     
  1494.     // The only way out of the while loop is EOF. Return FALSE if
  1495.     // no topic was extracted.
  1496.  
  1497.     CheckCurTag(in, log, out, fmt, run, FALSE);
  1498.  
  1499.     return !(in.m_listTags.IsEmpty());
  1500. }
  1501.  
  1502.  
  1503.